package gameserver;

/*
 * This program is free software: you can redistribute it and/or modify it under
 * the terms of the GNU General Public License as published by the Free Software
 * Foundation, either version 3 of the License, or (at your option) any later
 * version.
 * 
 * This program is distributed in the hope that it will be useful, but WITHOUT
 * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
 * FOR A PARTICULAR PURPOSE. See the GNU General Public License for more
 * details.
 * 
 * You should have received a copy of the GNU General Public License along with
 * this program. If not, see <http://www.gnu.org/licenses/>.
 * 
 * Credit goes to l2j-client project <https://code.google.com/p/l2jserver-client/> and l2j project <http://www.l2jserver.com/>
 */

import java.io.DataInputStream;
import java.io.DataOutputStream;
import java.io.IOException;
import java.net.InetAddress;
import java.net.ServerSocket;
import java.net.Socket;
import java.net.UnknownHostException;

public class GameServerToClient extends Thread
{
	@Override
	public void run()
	{
		ServerSocket client;
		try
		{
			client = new ServerSocket(7777, 50, InetAddress.getByName("127.0.0.1"));
			while (true)
			{
				Socket clientSocket = client.accept();
				System.out.println("GameServerToClient: client accepted");
				ServerToClientThread clientThread = new ServerToClientThread(clientSocket);
				clientThread.start();
			}
		}
		catch (Exception e)
		{
			System.out.println("GameServerToClient: client's accepting: " + e.getLocalizedMessage());
		}

	}
}

class ServerToClientThread extends Thread
{
	private static Socket serverSocket;
	private static Socket clientSocket;
	private static DataInputStream fromServer;
	private static DataOutputStream toClient;
	private static GameCrypt crypt = null;
	private static GameServerPacket gsp;

	public ServerToClientThread(Socket client)
	{
		clientSocket = client;
	}

	@Override
	public void run()
	{
		try
		{
			toClient = new DataOutputStream(clientSocket.getOutputStream());
			serverSocket = new Socket("64.25.37.133", 7777);
			fromServer = new DataInputStream(serverSocket.getInputStream());
			GameClientToServer thread = new GameClientToServer(clientSocket, serverSocket);
			thread.start();
			gsp = new GameServerPacket();
			// ----------------------------------------------------------

			while (serverSocket.isConnected() && !serverSocket.isClosed() && clientSocket.isConnected() && !clientSocket.isClosed())
			{

				byte[] buf = new byte[2];
				int read = fromServer.read(buf);
				if (read < 0)
				{
					if (serverSocket != null && serverSocket.isConnected())
						serverSocket.close();
					if (clientSocket != null && clientSocket.isConnected())
						clientSocket.close();
					System.out.println("GameServerToClient: run(): Disconnected due to eond of stream in read");
					return;
				}
				else if (read != 2)
				{
					System.out.println("GameServerToClient: run(): Read less than 2 bytes for packet length, skipping info");
					continue;
				}
				int rawSize = Sbyte2int(buf[0]) + Sbyte2int(buf[1]) * 256;

				if (rawSize <= 0)
					continue;

				byte[] data = new byte[rawSize];

				read = fromServer.read(data, 2, rawSize - 2);
				if (read < 0)
				{
					if (serverSocket != null && serverSocket.isConnected())
						serverSocket.close();
					if (clientSocket != null && clientSocket.isConnected())
						clientSocket.close();
					System.out.println("GameServerToClient: data: Disconnected due to eond of stream in read");
					return;
				}
				else
				{
					if (read != rawSize - 2)
					{
						System.out.println("Stream read returned only " + read + " of " + (rawSize - 2) + " bytes in total");
						while (read != rawSize - 2)
							read += fromServer.read(data, 2 + read, rawSize - 2 - read);

						System.out.println("Stream finally read " + read + " bytes of " + (rawSize - 2) + " bytes in total");
					}
				}

				try
				{
					data[0] = buf[0];
					data[1] = buf[1];
				}
				catch (ArrayIndexOutOfBoundsException e)
				{
					System.out.println("GameServerToClient: " + e.getLocalizedMessage());
					if (clientSocket.isConnected())
						clientSocket.close();
					return;
				}

				if (crypt == null)
				{
					toClient.write(data);
					crypt = new GameCrypt(null);
					crypt.decrypt(data, 2, data.length - 2);
					gsp.setBytes(data);
					if (gsp.readC() != 0)
					{
						byte[] key = new byte[16];
						for (int i = 0; i < 8; i++)
						{
							key[i] = (byte) gsp.readC();
						}
						key[8] = (byte) 0xc8;
						key[9] = (byte) 0x27;
						key[10] = (byte) 0x93;
						key[11] = (byte) 0x01;
						key[12] = (byte) 0xa1;
						key[13] = (byte) 0x6c;
						key[14] = (byte) 0x31;
						key[15] = (byte) 0x97;
						crypt = new GameCrypt(key);
					}
					else
					{
						if (clientSocket.isConnected())
							clientSocket.close();
						System.out.println("Wrong protocol.");
						return;
					}
				}
				else
				{
					crypt.decrypt(data, 2, data.length - 2);
					crypt.encrypt(data, 2, data.length - 2);
					toClient.write(data);
					toClient.flush();
					/*
					 * short cde = (short) (data[2] & 0xff); short ex = 0; if ((cde & 0xFF) == 0xfe && data.length >= 4) ex = (short) ((data[4] << 8) | (data[3] & 0xff));
					 */

				}
			}
		}
		catch (Exception e)
		{
			System.out.println("GameServerToClient: run(): " + e.getLocalizedMessage());
		}
		System.out.println("GameServerToClient: end");
	}

	private static String byteArrayToHexString(byte in[])
	{
		String rslt = "";
		String thes = "";
		for (int iuo = 0; iuo < in.length; iuo++)
		{
			int wtrf = in[iuo];
			if (wtrf < 0)
			{
				wtrf = 256 + wtrf;
			}
			// rslt += " " + (in[iuo]);

			thes = Integer.toHexString(wtrf);
			if (thes.length() < 2)
			{
				thes = "0" + thes;
			}
			rslt += " " + thes;
		}
		return rslt.toUpperCase();
	}

	private static int Sbyte2int(byte sb)
	{
		int wtrf = sb;
		if (wtrf < 0)
		{
			wtrf = 256 + wtrf;
		}
		return wtrf;
	}
}